package lexicalAnalysis.core;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

public class LexicalAnalyzer {
	private Reader reader;
	private final int start = 0;
	private final int Number = 1;
	private final int id = 2;
	private final int EQ = 3; // =
	private final int GT = 4; // >
	private final int LT = 5; // <
	private final int finish = 7;
	private final int error = 8;

	private String[] keywords = { "if", "else", "int" };

	public LexicalAnalyzer(String filePath) {
		reader = new Reader(new File(filePath));
	}
	
	public static void main(String[] args){
		LexicalAnalyzer analyzer=new LexicalAnalyzer(System.getProperty("user.dir")+"/src/lexicalAnalysis/input");
		analyzer.begin();
	}
	
	public List<Token> getTokens(){
		List<Token> tokens=new LinkedList<>();
		while (!reader.finish()) {
			tokens.add(getNextToken(false));
		}
		return tokens;
	}

	public Token getNextToken(boolean print) {
		Token token = new Token();
		int currentState = start;
		StringBuilder tokenMatcher = new StringBuilder();
		boolean isSave;
		String tokenType = "";
		String tokenAttri="";
		while (currentState != finish && currentState != error) {
			char c = nextChar();
			isSave = true;
			switch (currentState) {
			case start:
				if (isBlank(c)) {
					isSave = false;
				} else if (isLetter(c)) {
					currentState = id;
				} else if (isDigit(c)) {
					currentState = Number;
				} else if (c == '=') {
					currentState = EQ;
				} else if (c == '>') {
					currentState = GT;
				} else if (c == '<') {
					currentState = LT;
				} else if (c == ';') {
					currentState = finish;
					tokenType = "Seperator";
				} else if (c == ';') {
					currentState = finish;
					tokenType = "Seperator";
				} else if (c == '(' || c == ')' || c == '{' || c == '}') {
					currentState = finish;
					tokenAttri=String.valueOf(c);
					tokenType = "special";
				} else if(c=='+'||c=='-'||c=='*'|c=='/'){
					currentState = finish;
					tokenAttri =String.valueOf(c);
					tokenType="operator";
				}
				else {
					currentState = error;
					System.out.println("error on " + c);
				}
				break;
			case Number:
				if(c==0){
					currentState = finish;
					tokenType = "number";
					isSave = false;
				}else if (!isDigit(c)) {
					currentState = finish;
					tokenType = "number";
					unGetChar();
					isSave = false;
				}
				break;
			case id:
				if(c==0){
					currentState = finish;
					tokenType = "id";
					isSave = false;
				}else if (!isLetter(c)) {
					currentState = finish;
					tokenType = "id";
					unGetChar();
					isSave = false;
				}
				break;
			case EQ:
				if(c==0){
					currentState = finish;
					tokenType = "assignment";
					isSave = false;
				}else if (c != '=') {
					currentState = finish;
					tokenType = "assignment";
					unGetChar();
					isSave = false;
				} else {
					currentState = finish;
					tokenType = "relop";
				}
				break;
			case LT:
				if(c==0){
					currentState = finish;
					tokenType = "relop";
					isSave = false;
				}else if (c != '=') {
					currentState = finish;
					tokenType = "relop";
					tokenAttri="<";
					unGetChar();
					isSave = false;
				} else {
					currentState = finish;
					tokenType = "relop";
					tokenAttri="<=";
				}
				break;
			case GT:
				if(c==0){
					currentState = finish;
					tokenType = "relop";
					isSave = false;
				}else if (c != '=') {
					currentState = finish;
					tokenType = "relop";
					tokenAttri=">";
					unGetChar();
					isSave = false;
				} else {
					currentState = finish;
					tokenType = "relop";
					tokenAttri=">=";
				}
				break;
			}
			if (isSave)
				tokenMatcher.append(c);
		}

		makeToken(token, tokenMatcher.toString(), tokenType,tokenAttri);
		if (print)
			printToken(token);
		return token;
	}

	public void begin() {
		System.out.println("词素"+"	"+"词法单元"+"	"+"属性值");
		while (!reader.finish()) {
			getNextToken(true);
		}
	}

	private void printToken(Token token) {
		System.out.println(token.getMatcher() + "	" + token.getName()+"	"+token.getAttribute());
	}

	private void makeToken(Token token, String tokenMatcher, String tokenType,String tokenAttri) {
		if (tokenType.equals("id")) {
			if (isKeywords(tokenMatcher)) {
				token.setMatcher(tokenMatcher);
				token.setName(tokenMatcher);
				token.setAttribute(tokenAttri);
			} else {
				token.setMatcher(tokenMatcher);
				token.setName(tokenType);
				token.setAttribute(tokenAttri);
			}
		} else {
			token.setName(tokenType);
			token.setMatcher(tokenMatcher);
			token.setAttribute(tokenAttri);
		}
	}

	private boolean isLetter(char c) {
		return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
	}

	private boolean isBlank(char c) {
		return c == ' ' || c == '\r' || c == '\t' || c == '\n';
	}

	private boolean isDigit(char c) {
		return c >= '0' && c <= '9';
	}

	private char nextChar() {
		return reader.getNextChar();
	}

	private void unGetChar() {
		reader.unGetChar();
	}

	private boolean isKeywords(String s) {
		boolean result = false;
		for (String i : keywords) {
			if (i.equals(s)) {
				result = true;
				break;
			}
		}
		return result;
	}
}
